using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using MongoDB;
using MongoDB.Bson;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
using MongoDB.Driver;
using MongoDB.Bson.Serialization;
using DapperQ.Mapping;
using LightSO.ILibrary;
using LightSO.ILibrary.Mapping;


namespace LightSO.Library.MongoDb
{
    /// <summary>
    /// 基于MongoDb的数据仓储类实现
    /// </summary>
    public class Repository<T> : IMongoRepository<T> where T : EntityBase, new()
    {
        private string _collectionName = string.Empty;
        public Repository()
        {
            var _att = Attribute.GetCustomAttribute(typeof(T), typeof(CollectionAttribute), true);
            string _collectionName = _att != null ? ((CollectionAttribute)_att).Name : typeof(T).Name;
        }
        public Repository(string collectionName) 
        {
            _collectionName = collectionName;
        }

        /// <summary>
        /// 获取MongoDB的Collection
        /// </summary>
        /// <returns>MongoDB的Collection</returns>
        public MongoCollection<T> GetCollection()
        {
            DbContext<T> db = new DbContext<T>();
            return db.GetCollection();
        }

        /// <summary>
        /// 获取MongoDB的Collection
        /// </summary>
        /// <returns>MongoDB的Collection</returns>
        public MongoCollection GetCollection(string collectionName)
        {
            DbContext<T> db = new DbContext<T>();
            return db.GetCollection(collectionName);
        }

        /// <summary>
        /// 支持Linq语法查询结果集对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IQueryable<T> AsQueryable()
        {
            DbContext<T> db = new DbContext<T>();
            if(string.IsNullOrWhiteSpace(_collectionName))
                return db.GetCollection().AsQueryable();
            else
                return db.GetCollection(_collectionName).AsQueryable<T>();
        }

        /// <summary>
        /// 根据条件表达式统计满足条件的数据条数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <returns></returns>
        public long Count(Expression<Func<T, bool>> expression)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            return q.Count();
        }

        /// <summary>
        /// 根据条件表达式判断是否存在符合条件的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <returns></returns>
        public bool Exists(Expression<Func<T, bool>> expression)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            return q.Any();
        }

        /// <summary>
        /// 根据条件表达式获取满足条件的前n条类型为T的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <param name="n">需获取的记录数</param>
        /// <returns></returns>
        public List<T> Top(Expression<Func<T, bool>> expression, int n)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            return q.Take(n).ToList();
        }

        /// <summary>
        /// 根据条件表达式获取满足条件的前n条类型为T自定义排序数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <param name="n">需获取的记录数</param>
        /// <param name="orderby">排序对象集合</param>
        /// <returns></returns>
        public List<T> Top(Expression<Func<T, bool>> expression, int n, params Expression<Func<T, bool>>[] orderby)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            foreach (var sort in orderby)
                q = q.OrderWith(sort);

            return q.Take(n).ToList();
        }

        /// <summary>
        /// 根据条件表达式获取满足条件的前n条类型为T自定义排序数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <param name="n">需获取的记录数</param>
        /// <param name="orderby">排序对象集合</param>
        /// <returns></returns>
        public List<T> Top(Expression<Func<T, bool>> expression, int n, params Expression<Func<T, object>>[] groupby)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            var grouplist = default(IQueryable<IGrouping<object, T>>);
            foreach (var group in groupby)
                grouplist = q.GroupBy(group);

            return q.Take(n).ToList();
        }
        /// <summary>
        /// 根据主键获取类型为T的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id">主键字段值</param>
        /// <returns></returns>
        public T FindOne(Expression<Func<T, bool>> expression)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            return q.FirstOrDefault(expression);
        }

        /// <summary>
        /// 根据条件表达式获取满足条件的所有类型为T的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <returns></returns>
        public List<T> Find(Expression<Func<T, bool>> expression)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            return q.ToList();
        }

        /// <summary>
        /// 根据条件表达式获取满足条件的所有类型为T的自定义排序数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <param name="groupby">排序对象集合</param>
        /// <returns></returns>
        public List<T> Find(Expression<Func<T, bool>> expression, params Expression<Func<T, bool>>[] orderby)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            foreach (var sort in orderby)
                q = q.OrderWith(sort);

            return q.ToList();
        }

        /// <summary>
        /// 根据条件表达式获取满足条件的所有类型为T的自定义分组数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">条件表达式</param>
        /// <param name="orderby">排序对象集合</param>
        /// <returns></returns>
        public List<T> Find(Expression<Func<T, bool>> expression, params Expression<Func<T, object>>[] groupby)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            var grouplist = default(IQueryable<IGrouping<object, T>>);
            foreach (var group in groupby)
                grouplist = q.OrderBy(group).GroupBy(group);

            return q.ToList();
        }

        /// <summary>
        /// 获取指定分页标准为T的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页数据量</param>
        /// <returns></returns>
        public IPageList<T> FindPage(int pageIndex, int pageSize)
        {
            return new PageList<T>(AsQueryable(), pageIndex, pageSize);
        }

        /// <summary>
        /// 根据条件表达式获取满足条件、指定分页标准的所有类型为T的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页数据量</param>
        /// <param name="expression">条件表达式</param>
        /// <returns></returns>
        public IPageList<T> FindPage(int pageIndex, int pageSize, Expression<Func<T, bool>> expression)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            return new PageList<T>(q, pageIndex, pageSize);
        }

        /// <summary>
        /// 根据条件表达式获取满足条件、指定分页标准的所有类型为T的自定义排序数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页数据量</param>
        /// <param name="expression">条件表达式</param>
        /// <param name="orderby">排序对象集合</param>
        /// <returns></returns>
        public IPageList<T> FindPage(int pageIndex, int pageSize, Expression<Func<T, bool>> expression, params Expression<Func<T, bool>>[] orderby)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            foreach (var sort in orderby)
                q = q.OrderWith(sort);

            return new PageList<T>(q, pageIndex, pageSize);
        }

        /// <summary>
        /// 根据条件表达式获取满足条件、指定分页标准的所有类型为T的自定义分组数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页数据量</param>
        /// <param name="expression">条件表达式</param>
        /// <param name="orderby">排序对象集合</param>
        /// <returns></returns>
        public IPageList<T> FindPage(int pageIndex, int pageSize, Expression<Func<T, bool>> expression, params Expression<Func<T, object>>[] groupby)
        {
            var q = AsQueryable();

            if (expression != null)
                q = q.Where(expression);

            var grouplist = default(IQueryable<IGrouping<object, T>>);
            foreach (var group in groupby)
                grouplist = q.GroupBy(group);

            return new PageList<T>(q, pageIndex, pageSize);
        }

        /// <summary>
        /// 保存新的类型为T的数据对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item">需保存的对象</param>
        /// <returns></returns>
        public T Insert(T item)
        {
            DbContext<T> db = new DbContext<T>();
            if (item.UUID.Equals(ObjectId.Empty))
                item.UUID = ObjectId.GenerateNewId();

            db.GetCollection().Insert<T>(item);

            return item;
        }

        /// <summary>
        /// 更新指定的类型为T的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item">需更新的对象</param>
        /// <param name="exp">条件表达式</param>
        public void Update(T item, Expression<Func<T, bool>> exp)
        {
            DbContext<T> db = new DbContext<T>();
            var query = Query<T>.Where(exp);
            var data = item.ToBsonDocument();
            data.Remove("_id");
            db.GetCollection().Update(query, new UpdateDocument { { "$set", data } }, UpdateFlags.Multi);
        }

        ///<summary>
        /// 保存指定的类型为T的对象
        /// (找到符合条件的第一条数据并更新，没有符合条件的数据则插入)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item">需更新的对象</param>
        /// <param name="exp">条件表达式</param>
        public void Save(T item, Expression<Func<T, bool>> exp)
        {
            DbContext<T> db = new DbContext<T>();
            var query = Query<T>.Where(exp);
            var data = item.ToBsonDocument();
            data.Remove("_id");
            db.GetCollection().Update(query, new UpdateDocument { { "$set", data } }, UpdateFlags.Upsert);
        }

        /// <summary>
        /// 删除某个类型为T的数据
        /// </summary>
        /// <typeparam name="T">IMongoQuery</typeparam>
        /// <param name="query">查询条件</param>
        public void Remove(Expression<Func<T, bool>> exp)
        {
            DbContext<T> db = new DbContext<T>();
            var query = Query<T>.Where(exp);
            db.GetCollection().Remove(query);
        }
    }
}
